<?php
namespace Tbf\Bundle\DatabaseBundle\ThinkPhpModel;
use Tbf\Component\Arrays\ArrayFunction;
use Tbf\Bundle\DatabaseBundle\Exception\DatabaseException;
/**
 * 仅封装curd请求,不简化调用
 * 灵活性高,同v1.1版db类(直接复制)
 * options格式：
 * i:insert
 * s:select
 * u:update
 * d:delete
 * table 表名 i s u d
 * data  数据 i u
 * field   s
 * where    s u d
 * group    s
 * order    s u d
 * limit    s u d
 * assoc    s
 * input    s u d

 * */
class CurdModel
{
    protected $db;
    public function __construct($tpdo)
    {
        $this->db = $tpdo;
    }
    /**
     * 插入
     @param array $options
     table:表名
     data:需要插入的数据,这个接口只能一次插入一行!
     1.{'user_id':1,'name':'hehe'}
     返回lastInsertId
     */
    public function insert($options=null)
    {
        ArrayFunction::completeKeys($options,
            array('data','table','debug'));
        if (empty($options['table'])) {
            throw new DatabaseException('require option "table" for curd.insert');
        }
        if (empty($options['data'])) {
            throw new DatabaseException('require option "data" for curd.insert');
        }

        $table = $this->_parseTable($options['table']);
        $data = $this->_parseInsertData($options['data']);
        $column = $data['column'];
        $value = $data['value'];
        $input = $data['input'];
        $sql = 'INSERT INTO '.$table.' '.$column.' VALUE '.$value;
        $ret = $this->db->query($sql,$input,$options['debug']);
        $last_insert_id = $this->db->lastInsertId();

        return $last_insert_id;
    }
    /**
     * 读数据
     @param array $options
     table:表名
     field:需要查询的项。
     1.默认为*
     2.'user_id,name' sql字符串
     3.['user_id','name':'n1'] === user_id,name AS n1
     where:限制条件
     1."user_id=1 AND name='hehe'" 字符串直接使用(不建议使用，1.安全问题，2.没有结构，可以解决框架没有覆盖到的功能)
     2.{'user_id':1,'name':'hehe'} 建议使用
     2，3默认以AND链接条件。
     3.高级功能(未实现)
     OR {'or':{'user_id':1,'name':'hehe'}}
     IN {'user_id':[1,2]}
     //> {'user_id >':5} 使用?实现该情况
     NOT IN {'not':{'user_id':[1,2]}
     ? {'read_count BETWEEN ? AND ?':[1,10]}
     group:聚类，sql字符串
     limit:显示个数，sql字符串
     assoc:返回结果按照xx重新生成关联数组
     1.默认为false 不处理返回结果，返回纯数组
     2.'user_id' 按字段'user_id'生成关联数组，出现重复产生严重异常。

     注意:在某些特殊用途下,可以不传入表名..
     */
    public function select($options=null)
    {
        ArrayFunction::completeKeys($options,
        array('field','table','where','group','order','limit','assoc','input','debug'));
        $expr = $this->_parseSelectField($options['field']);
        if (empty($options['table'])) {
            throw new DatabaseException('require option "table" for curd.select');
        }
        $table = $this->_parseTable($options['table']);
        list($where,$winput) = $this->_parseWhere($options['where']);
        $group = $this->_parseGroup($options['group']);
        $order = $this->_parseOrder($options['order']);
        //$have = $options['have'];
        $limit = $this->_parseLimit($options['limit']);
        $assoc = $this->_parseAssoc($options['assoc']);
        $input = $this->_parseInput($options['input']);
        $sql = 'SELECT '.$expr;
        if ($table) {
            $sql .= ' FROM '.$table;
        }
        if ($where) {
            $sql.=' WHERE '.$where;
            $input = array_merge($winput,$input);
        }
        if ($group) {
            $sql.=' GROUP BY '.$group;
        }
        if ($order) {
            $sql.=' ORDER BY '.$order;
        }
        if ($limit) {
            $sql.=' LIMIT '.$limit;
        }
        $data = $this->db->query($sql,$input,$options['debug']);
        if ($assoc) {
            $data = ArrayFunction::rekeyById($data,$assoc);
        }

        return $data;
    }
    /**
     * 更新数据
     * 如果更新的那些数据，不存在，会依然成功。（mysql也是这样。）
     * 更新需要写where!!!忘记写了后果很严重!
     * 如果有特殊需要请手写sql...
      @param unknown_type $options
      @return true
     */
    public function update($options=null)
    {
        ArrayFunction::completeKeys($options,
        array('table','where','data','order','limit','input','debug'));
        if (empty($options['table'])) {
            throw new DatabaseException('require option "table" for curd.update');
        }
        //忘记写where后果很严重
        if (empty($options['where'])) {
            throw new DatabaseException('require option "where" for curd.update');
        }
        if (empty($options['data'])) {
            throw new DatabaseException('require option "data" for curd.update');
        }

        $table = $this->_parseTable($options['table']);
        list($expr,$dinput) = $this->_parseUpdateData($options['data']);
        list($where,$winput) = $this->_parseWhere($options['where']);
        $order = $this->_parseOrder($options['order']);
        $limit = $this->_parseSingleLimit($options['limit']);
        $input = $this->_parseInput($options['input']);
        $sql = 'UPDATE '.$table.' SET '.$expr;
        if ($where) {
            $sql.=' WHERE '.$where;
        } else {
            throw new DatabaseException('require option "where" for curd.update');
        }
        if ($order) {
            $sql.=' ORDER BY '.$order;
        }
        if ($limit) {
            $sql.=' LIMIT '.$limit;
        }
        $input = array_merge($input,$dinput,$winput);
        $ret = $this->db->query($sql,$input,$options['debug']);

        return $ret;
    }
    /**
     * 删除数据
     * 删除需要写where!!忘记写了后果很严重!
     * 如果有其他特殊需要请手写sql..
     * */
    public function delete($options=null)
    {
        ArrayFunction::completeKeys($options,
        array('table','where','order','limit','input','debug'));
        if (empty($options['table'])) {
            throw new DatabaseException('require option "table" for curd.delete');
        }
        //忘记写where后果很严重
        if (empty($options['where'])) {
            throw new DatabaseException('require option "where" for curd.delete');
        }
        $table = $this->_parseTable($options['table']);
        list($where,$winput) = $this->_parseWhere($options['where']);
        $order = $this->_parseOrder($options['order']);
        $limit = $this->_parseSingleLimit($options['limit']);
        $input = $this->_parseInput($options['input']);
        $sql = 'DELETE FROM '.$table;
        if ($where) {
            $sql.=' WHERE '.$where;
        } else {
            throw new DatabaseException('require option "where" for curd.delete');
        }
        if ($order) {
            $sql.=' ORDER BY '.$order;
        }
        if ($limit) {
            $sql.=' LIMIT '.$limit;
        }
        $input = array_merge($input,$winput);
        $ret = $this->db->query($sql,$input,$options['debug']);

        return $ret;
    }
    /**
     * 分析表名
     @param unknown_type $table
     */
    protected function _parseTable($table)
    {
        return $table;
    }
    /**
     * 分析插入数据
     * 对字段名进行了分析
     @param array $data
     输入：
     {'id':1,'name':'hehe'}
     */
    protected function _parseInsertData($data)
    {
        $columnName = array_keys($data);
        $numColumn = count($data);
        $columnString = '(`'.implode('`,`',$columnName).'`)';
        $valueString = '('.self::repeatString('?', $numColumn).')';
        $inputArray = array_values($data);

        return array(
                'column'=>$columnString,
                'value'=>$valueString,
                'input'=>$inputArray);
    }
    /**
     * 分析更新数据
     @param array $data
     支持下面2种输入：
     {'id':1,'name':'hehe','pass=123'}
     "id=id+1,name='hehe'"
     */
    protected function _parseUpdateData($data)
    {
        $input = array();
        if (is_string($data)||$data===null) {
            return array($data,$input);
        }
        foreach ($data as $k1=>&$v1) {
            if (ctype_digit($k1)||is_int($k1)) {
                continue;
            }
            $input[] = $v1;
            $v1 = '`'.$k1.'`=?';
        }
        $output = implode(',',$data);

        return array($output,$input);
    }
    protected function _parseSelectField($field)
    {
        if (is_string($field)) {
            return $field;
        }
        if ($field===null) {
            return '*';
        }
        foreach ($field as $k1=>$v1) {
            if (is_int($k1)||ctype_digit($k1)) {
                continue;
            }
            $field[$k1] = $k1.' AS '.$v1;
        }

        return implode(',',$field);
    }
    /**
     * 处理where表达式
     * 输入 方便的where表达式
     *   1.字符串，直接返回
     *   2.数组
     * 返回
     *   array(where字符串（含?）,?里面的数据)
     *
     * 注意:
     *   需要把生成的表达式发放在前面,不与用户输入的数据冲突
     */
    protected function _parseWhere($where)
    {
        $input = array();
        //字符串
        if (is_string($where)||$where===null) {
            return array($where,$input);
        }
        $create_where = array();
        $user_where = array();
        foreach ($where as $k1=>$v1) {
            //没有字段名
            if (ctype_digit($k1)||is_int($k1)) {
                $user_where[] = $v1;
                continue;
            }
            $key = $this->handleField($k1);
            //in 的情况
            if (is_array($v1)) {
                if (empty($v1)) {
                    //空数组 表示不想取出东西
                    $create_where[] = '1!=1';
                    continue;
                }
                $num = count($v1);
                $create_where[$k1] = $key.' IN ('.self::repeatString('?',$num).')';
                foreach ($v1 as $v2) {
                    $input[] = $v2;
                }
                continue;
            }
            $input[] = $v1;
            $create_where[$k1] = $key.'=?';
        }
        $output_where = array_merge($create_where,$user_where);
        $output = implode(' AND ',$output_where);

        return array($output,$input);
    }
    protected function _parseGroup($group)
    {
        if (is_string($group)||$group===null) {
            return $group;
        }
        throw new DatabaseException('not implement');
    }
    protected function _parseOrder($order)
    {
        if (is_string($order)||$order===null) {
            return $order;
        }
        throw new DatabaseException('not implement');
    }
    /**
     * 可能是注入点
     * */
    protected function _parseLimit($limit)
    {
        if (is_string($limit)||
            is_int($limit)||
            $limit===null){
            return $limit;
        }
        throw new DatabaseException('not understand option "limit"');
    }
    /**
     * 可能是注入点
     * */
    protected function _parseSingleLimit($limit)
    {
        if (is_string($limit)||
            is_int($limit)||
            $limit===null){
            return $limit;
        }
        throw new DatabaseException('not understand option "limit"');
    }
    protected function _parseAssoc($assoc)
    {
        if ($assoc===null||is_string($assoc)) {
            return $assoc;
        }
        throw new DatabaseException('not understand option "assoc"');
    }
    protected function _parseInput($input)
    {
        if ($input===null) {
            return array();
        }
        if (is_array($input)) {
            return $input;
        }
        throw new DatabaseException('not understand option "input"');
    }
    /**
     * 处理字段名
     */
    protected function handleField($field)
    {
        return $this->db->quoteFieldName($field);
    }
    /**
     * 生成逗号分割的重复字符串
     */
    private static function repeatString($repeat,$multiplier,$glue=',')
    {
        return str_repeat($repeat.$glue, $multiplier-1).$repeat;
    }

}
